package main

import (
	"sync"
	"fmt"
)

// GoroutinesPool内部维护一组Worker,接收到外部任务，向空闲Worker发送任务
type GoroutinesPool struct {
	workers       []*worker     // 所有Worker列表
	idles         chan *worker  // 空闲Worker列表
	tasksQueue    chan *Task    // 待调度任务列表
	waitCompleted chan struct{} // 终止状态
}

// 任务
type Task struct {
	id  int
	fcn func()
}

// 启动一个内部循环子协程来处理调度任务
func (gp *GoroutinesPool) Start() {
	go func() {

		defer close(gp.waitCompleted)

		for task := range gp.tasksQueue {
			// 取空闲Worker，发任务给它处理
			worker := <-gp.idles
			worker.tasks <- task
		}
		// 停止内部循环后，关闭Workers
		for _, worker := range gp.workers {
			close(worker.tasks)
			<-worker.waitExit
		}

	}()
}

// 关闭GoroutinesPool，阻塞等待所有Worker协程完成退出后，此函数才返回。
func (gp *GoroutinesPool) Shutdown() {
	close(gp.tasksQueue)
	// 等待所有任务完成
	<-gp.waitCompleted
}

// 当GoroutinesPool有空闲Worker协程时，任务将被调度执行。否则，此函数将被阻塞，直到有空闲的Worker协程。
func (gp *GoroutinesPool) Add(task *Task) {
	gp.tasksQueue <- task
}

// NewGoesPool 创建一个GoroutinesPool新对象指针，指定内部Worker协程的数量
func NewGoroutinesPool(numWorkers int) *GoroutinesPool {
	//tasks队列为worker的一半
	taskQueueSize := numWorkers/2
	//初始化线程池
	gp := &GoroutinesPool{
		workers:       make([]*worker, numWorkers),
		idles:         make(chan *worker, numWorkers),
		tasksQueue:    make(chan *Task, taskQueueSize),
		waitCompleted: make(chan struct{}),
	}

	for i := 0; i < numWorkers; i++ {
		// 初始化协程池的worker
		gp.workers[i] = newWorker(i)
		// 将worker加入空闲列表
		gp.idles <- gp.workers[i]
		// 每个worker开启工作协程
		go gp.workers[i].work(gp.idles)
	}

	return gp
}


// Worker包含一个任务列表和停止控制信号
type worker struct {
	id       int
	tasks    chan *Task    // 任务列表
	waitExit chan struct{} // 运行状态
}

// 启动内部协程，异步循环处理任务列表
func (w *worker) work(idles chan<- *worker) {
	defer close(w.waitExit)

	//定义任务完成后将worker加入空闲列表
	finishTask := func() {
		idles <- w
	}

	for task := range w.tasks {
		func() {
			// 完成一个任务后，将当前Worker恢复到空闲状态
			defer finishTask()
			task.fcn()
			fmt.Println("worker:", w.id, " task:", task.id)
		}()
	}
}

func newWorker(id int) *worker {
	return &worker{
		id:       id, // 标识
		tasks:    make(chan *Task,1), // 每次做1任务
		waitExit: make(chan struct{}),
	}
}

func main() {
	sum := 0
	// 指定协程池创建10个工作协程
	pool := NewGoroutinesPool(5)
	// 启动
	pool.Start()
	// 停止协程池，阻塞等待所有任务函数执行完毕后返回。
	defer pool.Shutdown()

	wg := new(sync.WaitGroup)
	//任务方法
	taskFunc := func() {
		sum++
		wg.Done()
	}

	for i := 0; i < 100; i++ {
		wg.Add(1)
		// 通过 Add 方法，向协程池添加等待调度的任务函数
		pool.Add(&Task{
			id: i,
			fcn: taskFunc,
		})
	}

	wg.Wait()

	fmt.Println(sum)
}
